#include "task/task_controller.h"
#include "utils/thread/thread_pool.h"
#include "task/task_chain.h"
#include <unordered_map>
#include <mutex>

namespace task
{
class TaskControllerData
{
public:
    explicit TaskControllerData(TaskController *task_controller)
        : task_controller_(task_controller)
    {}
private:
    friend class TaskController;
    TaskController *task_controller_{nullptr};
    base::ThreadPool thread_pool_;
    std::mutex task_chain_mutex_;
    std::unordered_map<std::string, TaskChain *> task_chain_map_;
    std::vector<exclude::ExcludePolicyConstPtr> start_exclude_policy_;
    std::vector<exclude::ExcludePolicyConstPtr> pause_exclude_policy_;
    std::vector<exclude::ExcludePolicyConstPtr> resume_exclude_policy_;
    std::vector<exclude::ExcludePolicyConstPtr> cancel_exclude_policy_;
    std::vector<exclude::ExcludePolicyConstPtr> done_exclude_policy_;

private:
    /**
     * @brief:  任务链结束后得回调
     * @author: sherlock_lht
     */
    void taskChainFinish(const std::string &name, const Context *context);

    /**
     * @brief:  依次执行所有的互斥策略，如果有一个策略执行失败，则中断执行
     * @author: sherlock_lht
     */
    bool executeExcludePolicy(const std::vector<exclude::ExcludePolicyConstPtr> &policys, task::TaskChain *task_chain);

    void taskEventNotify(const std::string &task_chain_name, task::TaskEventType task_event_type);
};
void TaskControllerData::taskChainFinish(const std::string &name, [[maybe_unused]] const Context *context)
{
    EASE_DEBUG("task control finish callback, task chain name: %s", name.c_str());
    TaskChain *task_chain = task_controller_->getTaskChain(name);
    if (nullptr == task_chain)
    {
        EASE_ERROR("task chain is not exist, finish callback error");
        return;
    }
    executeExcludePolicy(done_exclude_policy_, task_chain);
    std::lock_guard<std::mutex> lock_guard(task_chain_mutex_);
    if (task_chain_map_.find(name) != task_chain_map_.end())
    {
        task_chain_map_.erase(name);
    }
    EASE_DEBUG("task control finish callback done, task chain name: %s", name.c_str());
}
bool TaskControllerData::executeExcludePolicy(const std::vector<exclude::ExcludePolicyConstPtr> &policys, task::TaskChain *task_chain)
{
    for (auto &policy: policys)
    {
        if (!policy->execPolicy(task_chain))
        {
            return false;
        }
    }
    return true;
}
void TaskControllerData::taskEventNotify(const std::string &task_chain_name, task::TaskEventType task_event_type)
{
    switch (task_event_type)
    {
        case task::TaskEventType::PAUSE:
            task_controller_->pauseTaskChain(task_chain_name);
            break;
        case task::TaskEventType::RESUME:
            task_controller_->resumeTaskChain(task_chain_name);
            break;
        case task::TaskEventType::CANCEL:
            task_controller_->cancelTaskChain(task_chain_name);
            break;
        default:
            EASE_ERROR("task control process task event notify, unknown task event type: %d", task_event_type);
            break;
    }
}
}

namespace task
{
TaskController::TaskController()
    : d(std::make_unique<TaskControllerData>(this))
{
}
TaskController::~TaskController()
{
    if (!d->thread_pool_.waitForDone(10))
    {
        EASE_ERROR("thread pool exit error");
    }
}
void TaskController::setMaxParallelTaskCount(int32_t count)
{
    d->thread_pool_.setMaxThreadCount(count);
}
int32_t TaskController::getMaxParallelTaskCount() const
{
    return d->thread_pool_.maxThreadCount();
}

void TaskController::appendStartExcludePolicy(const exclude::ExcludePolicyConstPtr &policy)
{
    d->start_exclude_policy_.push_back(policy);
}
void TaskController::appendPauseExcludePolicy(const exclude::ExcludePolicyConstPtr &policy)
{
    d->pause_exclude_policy_.push_back(policy);
}
void TaskController::appendResumeExcludePolicy(const exclude::ExcludePolicyConstPtr &policy)
{
    d->resume_exclude_policy_.push_back(policy);
}
void TaskController::appendCancelExcludePolicy(const exclude::ExcludePolicyConstPtr &policy)
{
    d->cancel_exclude_policy_.push_back(policy);
}
void TaskController::appendDoneExcludePolicy(const exclude::ExcludePolicyConstPtr &policy)
{
    d->done_exclude_policy_.push_back(policy);
}
std::vector<TaskChain *> TaskController::getCurrentTaskChain() const
{
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    std::vector<TaskChain *> task_chain_vec;
    for (auto &pair: d->task_chain_map_)
    {
        task_chain_vec.push_back(pair.second);
    }
    return task_chain_vec;
}
std::vector<std::string> TaskController::getCurrentTaskChainName() const
{
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    std::vector<std::string> task_chain_name_vec;
    for (auto &pair: d->task_chain_map_)
    {
        task_chain_name_vec.push_back(pair.first);
    }
    return task_chain_name_vec;
}
bool TaskController::startTaskChain(TaskChain *task_chain)
{
    if (!d->executeExcludePolicy(d->start_exclude_policy_, task_chain))
    {
        EASE_ERROR("task chain name: %s, have task exclusion, cannot start", task_chain->getName().c_str());
        if (task_chain->autoDelete())
        {
            task_chain->execFinishCallback();
            delete task_chain;
        }
        return false;
    }
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    if (d->task_chain_map_.find(task_chain->getName()) != d->task_chain_map_.end())
    {
        EASE_ERROR("task chain name: %s is exist, cannot start repeatedly", task_chain->getName().c_str());
        if (task_chain->autoDelete())
        {
            task_chain->execFinishCallback();
            delete task_chain;
        }
        return false;
    }
    //这里特殊，任务链没有开始，则不会执行taskChainFinish
    task_chain->setFinishCallback(std::bind(&TaskControllerData::taskChainFinish, d.get(), std::placeholders::_1, std::placeholders::_2));
    task_chain->setTaskEventNotify(std::bind(&TaskControllerData::taskEventNotify, d.get(), std::placeholders::_1, std::placeholders::_2));
    d->thread_pool_.start(task_chain);
    d->task_chain_map_.emplace(task_chain->getName(), task_chain);

    return true;
}
bool TaskController::cancelTaskChain(const std::string &task_chain_name)
{
    TaskChain *task_chain = getTaskChain(task_chain_name);
    if (nullptr == task_chain)
    {
        EASE_ERROR("task chain is not exist, cannot cancel");
        return false;
    }
    if (!d->executeExcludePolicy(d->cancel_exclude_policy_, task_chain))
    {
        EASE_ERROR("task chain name: %s, have task exclusion, cannot cancel", task_chain->getName().c_str());
        return false;
    }
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    auto it = d->task_chain_map_.find(task_chain_name);
    if (it != d->task_chain_map_.end())
    {
        it->second->cancel();
        return true;
    }
    EASE_ERROR("task chain name: %s is not exist, cannot cancel repeatedly", task_chain_name.c_str());
    return false;
}

bool TaskController::pauseTaskChain(const std::string &task_chain_name)
{
    TaskChain *task_chain = getTaskChain(task_chain_name);
    if (nullptr == task_chain)
    {
        EASE_ERROR("task chain is not exist, cannot pause");
        return false;
    }
    if (!d->executeExcludePolicy(d->pause_exclude_policy_, task_chain))
    {
        EASE_ERROR("task chain name: %s, have task exclusion, cannot pause", task_chain->getName().c_str());
        return false;
    }
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    auto it = d->task_chain_map_.find(task_chain_name);
    if (it != d->task_chain_map_.end())
    {
        it->second->pause();
        return true;
    }
    EASE_ERROR("task chain name: %s is not exist, cannot pause", task_chain_name.c_str());
    return false;
}

bool TaskController::resumeTaskChain(const std::string &task_chain_name)
{
    TaskChain *task_chain = getTaskChain(task_chain_name);
    if (nullptr == task_chain)
    {
        EASE_ERROR("task chain is not exist, cannot resume");
        return false;
    }
    if (!d->executeExcludePolicy(d->resume_exclude_policy_, task_chain))
    {
        EASE_ERROR("task chain name: %s, have task exclusion, cannot resume", task_chain->getName().c_str());
        return false;
    }
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    auto it = d->task_chain_map_.find(task_chain_name);
    if (it != d->task_chain_map_.end())
    {
        it->second->resume();
        return true;
    }
    EASE_ERROR("task chain name: %s is not exist, cannot resume", task_chain_name.c_str());
    return false;
}
task::TaskChainStatus TaskController::getTaskChainStatue(const std::string &task_chain_name)
{
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    auto it = d->task_chain_map_.find(task_chain_name);
    if (it != d->task_chain_map_.end())
    {
        return it->second->getTaskStatus();
    }
    EASE_ERROR("task chain name: %s is not exist, get status fail", task_chain_name.c_str());
    return task::TaskChainStatus::TaskChainStatusIdle;
}
task::TaskChain *TaskController::getTaskChain(const std::string &task_chain_name)
{
    std::lock_guard<std::mutex> lock_guard(d->task_chain_mutex_);
    auto it = d->task_chain_map_.find(task_chain_name);
    if (it != d->task_chain_map_.end())
    {
        return it->second;
    }
    return nullptr;
}
}
